home *** CD-ROM | disk | FTP | other *** search
- ; /-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\
- ; | |
- ; | Terminal in assembler, A86 format |
- ; | Interrupt driven reception |
- ; | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
- ; | Weefus on GEnie or |
- ; | Keith Rolland 1:321/112.5 |
- ; | (!) Copywrong 1988 |
- ; | |
- ; \-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-/
- ;
- ;
- jmp BEGIN_PRG ;jump over our data area to the real beginning
-
- MESSAGE1 db 07h,0dh,0ah,0ah,0ah,"Press <alt>-q to quit.......",0dh,0ah,0ah,"$"
- MESSAGE2 db " Arrgg, I spent HOW much time on this?!? $"
-
- BUFFER_BOT equ $
- BUFFER db 0ffh DUP 00h ;the actual incoming buffer, 255 bytes
- BUFFER_TOP equ $
-
- BUFFER_TAIL dw 0000h ;used to control where characters are put
- BUFFER_HEAD dw 0000h ;into and taken from the buffer
-
- LINE_CTL_REG = 03fbh ;various memory locations for rs232 registers
- MODEM_CTL_REG = 03fch
- LINE_STAT_REG = 03fdh
- MODEM_STAT_REG = 03feh
- CHAR_HOLDING_REG = 03f8h
-
- BOX_TLCOR = 0c9h ;Ascii graphic characters used for
- BOX_TRCOR = 0bbh ;making the box.
- BOX_BLCOR = 0c8H ;TLCOR = TopLeftCORner and so on....
- BOX_BRCOR = 0bch
- BOX_VERT = 0bah
- BOX_HORZ = 0cdh
- SPACE = 020h
-
- SCROLL_COUNT db 00h
- ;============================================================================
- BEGIN_PRG:
- mov w[BUFFER_TAIL], offset BUFFER ;set up buffer pointer variables
- mov w[BUFFER_HEAD], offset BUFFER ;for our circular buffer
-
- call CLEAR_SCREEN ;routine that will....... (guess :)
-
- call BOX_MAKE ;make our superneato ascii box and message
-
- call SETUP ;set up rs232 and new interrupt routines
-
- ;*****************************************************************************
- MAIN_LOOP:
- call CHECK_INCOMING ;check the buffer, and print any waiting characters
- call CHECK_OUTGOING ;see if any keystrokes are waiting in the keyboard queue
- jnc MAIN_LOOP ;if carry flag not set, jump to main loop
- ;*****************************************************************************
- cli ;the carry flag is set, indicating an exit request
- in al,021h ;reset the com1 interrupt bit in the IMR
- or al,010h ;so that no more com1 ints are performed
- out 021h,al ;put it back
- mov dx,LINE_CTL_REG ;line control reg
- in al,dx ;get byte
- and al,07fh ;clear bit 7, com1 interrupt enable bit
- out dx,al ;put it back
- mov al,00h ;tell the modem we no longer will be operating.....
- mov dx,MODEM_CTL_REG ;clear modem control reg
- out dx,al ;do it
- sti ;re-enable interrupts
- int 20h ;standard end program interrupt
- ;-----------------------
- SETUP: ;direct set-up of registers, no bios or dos calls
- ;my mom said it was ok............:)
- mov dx,LINE_CTL_REG ;point to the line control register, it controls a lot.
- mov al,080h ;turn on bit 7
- out dx,al ;send byte
- dec dx ;point to msb of baud rate divisor
- dec dx
- mov al,00h ;msb for any rate > 1200 baud is 00h
- out dx,al ;send byte
- dec dx ;point to lsb of baud rate divisor
- mov al,060h ;lsb for 1200 bps is 60h, use 30h for 2400 bps
- out dx,al ;send byte
- mov dx,LINE_CTL_REG ;line control register
- mov al,0ah ;7/E/1 settings=0ah /use 03h for 8/n/1
- out dx,al ;send byte
-
- mov dx,offset NEW_INT ;point com1 interrupt to our new routine, NEW_INT
- mov al,0ch ;interrupt to replace = COM1
- mov ah,025h ;dos function number for change-a-vector
- int 21h ;call dos to change vector
-
- in al,021h ;this will enable interrupts by setting the
- and al,0efh ;com1 interrupt bit in the IMR (don't ask! :)
- out 021h,al ;IMR is the Interrupt Mask Register
- cli ;we don't want to be disturbed during this...
- mov dx,LINE_CTL_REG ;point to line control register, rs232
- in al,dx ;get byte
- and al,07fh ;set high bit
- out dx,al ;and put it back
- dec dx
- dec dx ;move to the rs232 interrupt enable register
- mov al,01h ;set it to interrupt when data recieved
- out dx,al ;put it back
- inc dx
- inc dx ;move to modem control register |could have done ....|
- inc dx ;set it for using interrupts |mov dx,MODEM_CTL_REG|
- mov al,08h ;bit #3 high
- out dx,al ;put it back
- sti ;re-enable normal interrupts
- mov dx,offset MESSAGE1 ;print how-to-exit message
- mov ah,09h ;via dos
- int 21h
- ret ;end of set-up
- ;-----------------------
- CHECK_INCOMING:
- mov bx,w[BUFFER_TAIL] ;check to see if anything
- cmp bx,w[BUFFER_HEAD] ;is in the buffer....
- je ret ;jump if nothing ready
- mov al,b[bx] ;character is ready, get it in al
- inc bx
- cmp bx,BUFFER_TOP ;if buffer_head is at the top of the
- jne >L17 ;buffer, we must point it to the other end
- mov bx,BUFFER_BOT
- L17:
- mov w[BUFFER_TAIL],bx ;place new buffer tail in storage
- cmp al,1bh ;is char an <escape>?
- jne >L13 ;no, so jump, otherwise.....
- mov al,0dbh ;change it to a graphic char. No vt100/ansi stuff allowed!
- L13: ; this is a *real* simple program! :)
- mov dl,al ;dos wants the char in dl to print it
- mov ah,02h ;dos print function
- int 21h ;call dos
- ret ;end check_incoming
- ;-----------------------
- CHECK_OUTGOING:
- call CK_FOR_KEY ;subroutine checks for keypress via bios interrupt
- je ret ;no key pressed, return to main_loop
- jnc >L4 ;no exit request pending so jump ahead
- ret ;carry set, so just return and main_loop will act on it
- L4:
- push bx ;push these on stack
- push dx ;as they will be used
- push cx ;here internally
- mov bl,al ;put keypress in bl temporarily
- mov dx,MODEM_CTL_REG ;point to modem control register
- mov al,0bh ;tell modem we want to send
- out dx,al ;put byte in the register
- mov dx,MODEM_STAT_REG ;point to modem status register
- WAIT_FOR_CLR:
- in al,dx ;get modem status
- test al,010h ;test for modems 'clear-to-send' line (bit #4)
- jne >L5 ;no problems so jump
- loop WAIT_FOR_CLR ;wait for ok (modem is presently busy)
- L5:
- mov dx,LINE_STAT_REG ;point to line status register
- OK_STATUS:
- in al,dx ;get status byte
- test al,020h ;test bit #5, 'transmit register empty'
- jne >L6 ;it's empty,so it's ready for another character
- loop OK_STATUS ;it is full, so loop until it is empty
- L6:
- mov al,bl ;put keypress in al
- mov dx,CHAR_HOLDING_REG ;point to transmitter holding register
- out dx,al ;put keypress in transmit register, where it
- pop cx ;automagicly will be sent upon it's way
- pop dx ;restore these from stack
- pop bx
- ret ;end check_outgoing
- ;-----------------------
- NEW_INT: ;Our New Interrupt Handler
- cli ;this interrupt routine is called by the hardware
- push ax ;every time a character is recieved via rs232
- push bx
- push dx ;save registers on stack
- push si
- push ds
- mov ax,cs ;make sure data seg is code seg
- mov ds,ax
- mov dx,CHAR_HOLDING_REG ;recieve register, the incoming char is held here
- in al,dx ;get byte, the incoming character, from register
- mov bx,w[BUFFER_HEAD] ;get present buffer head position in bx
- mov si,bx ;and then increase bx to point to where
- inc bx ;the next character will be placed
- cmp bx,BUFFER_TOP ;if BUFFER_HEAD=BUFFER_TOP, we make it BUFFER_BOT
- jne >L10 ;next char goes somewhere in the middle of the buffer
- mov bx,BUFFER_BOT ;here we reset BUFFER_HEAD to BUFFER_BOT
- L10:
- cmp bx,w[BUFFER_TAIL] ;if BUFFER_HEAD=BUFFER_TAIL, we have no more room
- je >L9 ;in the buffer, ie a buffer overrun has occured!
- ;in this case, we don't store the incoming char.
- ;ideally, at this point we should call an error
- ;handling routine, but you can write that. :)
- mov b[si],al ;put byte in buffer if everything is ok
- mov w[BUFFER_HEAD],bx ;point buffer_head to new head-of-buffer
- L9:
- mov al,020h ;tell the hardware we are through with this interrupt
- out 020h,al ;by doing this. Only with hardware related ints.
- pop ds
- pop si
- pop dx
- pop bx
- pop ax
- sti
- iret ;end new_int
- ;-----------------------
- CK_FOR_KEY:
- mov ah,01h ;use int 16h function #01 to test for
- int 16h ;keypress available
- je ret ;none ready yet so return
- pushf
- mov ah,00h ;use function #0 to get the keypress
- int 16h
- popf
- clc ;clear it before testing
- cmp al,00h ;is the keypress an extended code?
- jne ret ;no, so return to check_outgoing
- cmp ah,010h ;we have an extended code now- is it <alt>-q?
- jne ret ;no, so we will let the odd code go through for now
- stc ;yes, it is <alt>-q, so we set the carry flag and return
- ret ;end ck_for_key
- ;-----------------------
- CLEAR_SCREEN: ;the hard way!
- mov ah,02h ;use bios video to
- mov bh,00h ;set cursor position to top left
- mov dx,00h ;of screen
- int 10h ;bios function 02h
- ;
- mov ah,09h ;now using function 09h
- mov cx,0800h ;print a space char 0800h times, ~2000d...
- mov al,20h ;20h = space char
- mov bl,07h ;attribute of character
- int 10h ;clear it!
- ;
- mov ah,02h ;now let's put the
- mov bh,00h ;cursor back up in
- mov dx,00h ;the upper left corner
- int 10h ;bios video function
- ret ;end clear_screen
- ;-------------------------------------------------
- ;*******************************
- ;* Following are used to make *
- ;* the ascii box. Is it worth *
- ;* all of the programming? *
- ;*******************************
- PRINT_MANY:
- mov ah,02h ;load dl with character to be printed
- PRINT_MORE:
- int 21h ;and cx with the number of times it
- dec cx
- jcxz ret ;should be printed
- jmp PRINT_MORE
- ;-----------------------
- PRINT_ONE:
- mov ah,02h ;load dl with character
- int 21h
- ret
- ;-----------------------
- POSITION:
- MOV dl,SPACE
- mov cx,0ah
- call PRINT_MANY
- ret
- ;-----------------------
- CR_LF:
- mov ah,02h
- mov dl,0dh
- int 21h
- mov dl,0ah
- int 21h
- ret
- ;-----------------------
- SIDES: ;symbols make for good reading!
- call POSITION
- mov dl,BOX_VERT
- call PRINT_ONE
- mov dl,SPACE
- mov cx,03ah
- call PRINT_MANY
- mov dl,BOX_VERT
- call PRINT_ONE
- call CR_LF
- ret
- ;-----------------------
- BOX_TOP:
- call POSITION
- mov dl,BOX_TLCOR
- call PRINT_ONE
- mov dl,BOX_HORZ
- mov cx,03ah
- call PRINT_MANY
- mov dl,BOX_TRCOR
- call PRINT_ONE
- call CR_LF
- ret
- ;-----------------------
- BOX_BOT:
- call POSITION
- mov dl,BOX_BLCOR
- call PRINT_ONE
- mov dl,BOX_HORZ
- mov cx,03ah
- call PRINT_MANY
- mov dl,BOX_BRCOR
- call PRINT_ONE
- ret
- ;-----------------------
- BOX_MAKE:
- call CR_LF
- call CR_LF
- call BOX_TOP
- call SIDES
- call POSITION
- mov dl,BOX_VERT
- call PRINT_ONE
- lea dx,MESSAGE2 ;set up for dos' print-string routine
- mov ah,09h ;dos function number for same
- int 21h ;do it
- mov dl,BOX_VERT
- call PRINT_ONE
- call CR_LF
- call SIDES
- call BOX_BOT
- call CR_LF
- call SCROLL_DOWN
- call SCROLL_UP
- ret ;end box_make. Not exactly elegant, huh? :)
- ;-----------------------
- SCROLL_UP:
- mov SCROLL_COUNT,0fh ;number of lines to scroll
- DO_SCROLL:
- mov ah,00h ;get time of day
- int 01ah ;tod interrupt
- add dx,01h ;delay value
- mov bx,dx ;store ending value in bx
- push ax
- push bx
- mov ah,06h ;scroll up using bios function 6
- mov al,01h ;number of rows to scroll up
- mov cx,00h ;cl=top left row cl=top left col
- mov dh,018h ;b r row
- mov dl,050h ;b r col
- mov bh,07h ;attribute of cleared line
- int 10h ;do it
- pop bx
- pop ax
- WAIT_HERE:
- int 01ah ;check tod to see if delay is over
- cmp dx,bx
- jne WAIT_HERE
- dec SCROLL_COUNT
- mov al,SCROLL_COUNT
- jnz DO_SCROLL
- ret
- ;-----------------------
- SCROLL_DOWN:
- mov SCROLL_COUNT,0fh ;number of lines to scroll
- DO_SCROLL2:
- mov ah,00h ;get time of day
- int 01ah ;tod interrupt
- add dx,01h ;delay value
- mov bx,dx ;store ending value in bx
- push ax
- push bx
- mov ah,07h ;scroll down using bios function 7
- mov al,01h ;number of rows to scroll down
- mov cx,00h ;cl=top left row cl=top left col
- mov dh,018h ;b r row
- mov dl,050h ;b r col
- mov bh,07h ;attribute of cleared line
- int 10h ;do it
- pop bx
- pop ax
- WAIT_HERE2:
- int 01ah ;check tod to see if delay is over
- cmp dx,bx
- jne WAIT_HERE2
- dec SCROLL_COUNT
- mov al,SCROLL_COUNT
- jnz DO_SCROLL2
- ret
- ;-----------------------